/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package deadlocksim.backend;

import deadlocksim.*;
import deadlocksim.backend.process.*;

import java.util.*;
import java.awt.event.ActionListener;
/**
 *
 * @author Jason Kabel
 */
public class SimClock
{
    private LinkedList<DeadlockProcess> registeredProcesses; // All processes associated with this clock; the master list
    private LinkedList<DeadlockProcess> activeProcesses; // Haven't halted yet
    private LinkedList<DeadlockProcess> runningProcesses; // Not done with current phase
    private Ticker ticker;
    private javax.swing.Timer timer;
    private boolean isTicking;
    private boolean phaseComplete;
    private int timeCounter;
    private DeadlockSim sim;
    
    
    public SimClock()
    {
        super();
        initVars();
    }
    
    public SimClock(int delayInMilliseconds)
    {
        this();
        initVars();
        setDelay(delayInMilliseconds);
    }
    
    public void setSim(DeadlockSim sim)
    {
        this.sim = sim;
    }
    
    
    public void deregisterAll()
    {
        LinkedList<DeadlockProcess> processes = new LinkedList<DeadlockProcess>(registeredProcesses);
        Iterator<DeadlockProcess> iter = processes.iterator();
        while(iter.hasNext())
        {
            deregisterProcess(iter.next());
        }
        activeProcesses.clear();
    }
    
    public void deregisterProcess(DeadlockProcess p)
    {
        if(runningProcesses.contains(p))
            runningProcesses.remove(p);
        if(registeredProcesses.contains(p))
            registeredProcesses.remove(p);
        if(activeProcesses.contains(p))
            activeProcesses.remove(p);
    }
    
    public int getTime()
    {
        return timeCounter;
    }
    
    public void notifyProcessesHalted()
    {
        sim.sendMessage("SimClock: All processes have halted.", false);
    }
    
    public boolean phaseComplete()
    {
        return phaseComplete;
    }
    
    public void processPhaseComplete(DeadlockProcess p) 
    {
        if(runningProcesses.contains(p))    
            runningProcesses.remove(p);
        if(runningProcesses.isEmpty())
        {
            sim.detectDeadlocks();
            sim.refreshDisplay();
            phaseComplete = true;
        }
    }
    
    public void processHalted(DeadlockProcess p)
    {
        sim.refreshDisplay();
        if(activeProcesses.contains(p))
            activeProcesses.remove(p);
        if(activeProcesses.isEmpty())
        {
            stopTicking();
            notifyProcessesHalted();
        }
    }
    
    public void registerProcess(DeadlockProcess p)
    {
        registeredProcesses.add(p);
    }
    
    public void reset()
    {
        stopTicking();
        timeCounter = 0;
        runningProcesses.clear();
        activeProcesses.clear();
        phaseComplete = true;
        //activeProcesses.addAll(registeredProcesses);
    }
    
    public void setActiveProcesses()
    {
        activeProcesses.addAll(registeredProcesses);
    }
    
    public void setDelay(int delayInMilliseconds)
    {
        timer.setDelay(delayInMilliseconds);
    }
    public void startTicking()
    {
        //activeProcesses.addAll(registeredProcesses);
        timer.start();
        isTicking = true;
    }
    public void stopTicking()
    {
        timer.stop();
        isTicking = false;
        //phaseComplete = true;
    }
    
    public void clockTick()
    {
        ticker.tick();
    }
    
    /////////// PRIVATE METHODS ////////////////////
    private void initVars()
    {
        timeCounter = 0;
        activeProcesses = new LinkedList<DeadlockProcess>();
        registeredProcesses = new LinkedList<DeadlockProcess>();
        runningProcesses = new LinkedList<DeadlockProcess>();
        ticker = new Ticker(this);
        
        // Default is 5000 milliseconds (5 seconds)
        //ccs: i changed to 1000 for gui sanity. 9 seconds is better than 45
        timer = new javax.swing.Timer(4000, ticker);
        timer.setInitialDelay(500);
        isTicking = false;
        phaseComplete = true;
    }
    
    // INNER CLASS TO SUPPORT TICKING
    private class Ticker implements ActionListener
    {
        SimClock daddyClock;
        
        public Ticker(  SimClock clock)
        {
            daddyClock = clock;
        }
        public void actionPerformed(java.awt.event.ActionEvent e)
        {
            tick();
        }
        
        public void tick()
        {
            if(! daddyClock.phaseComplete())
            {
                System.err.println("Ticker waiting for next tick; processes still executing.");
                sim.sendMessage("Ticker waiting for next tick; processes still executing.",false);
                return;
            }
            else
            {
                sim.sendMessage("", false);
                sim.sendMessage("Time Start: " + (daddyClock.getTime()+1), false);
                daddyClock.timeCounter++; // Increment the time for this clock
                daddyClock.phaseComplete = false;
                Iterator<DeadlockProcess> iter = daddyClock.registeredProcesses.iterator();
                while(iter.hasNext())
                {
                    DeadlockProcess nextP = iter.next();
                    runningProcesses.add(nextP);
                    nextP.executePhase();
                }
                //daddyClock.phaseComplete = true;
            }
        }
    }

}
